package edu.upenn.seas.peerreview.smui

import edu.upenn.seas.peerreview.{Id, Source, Sink}

/**
 * The outward facing interface for the SM-UI layer. Main application code (e.g. 
 * Tester.scala) should use the Battleship object's factory methods to construct 
 * desired items.
 */


/**
 * Enum: the kind of state machine.
 */
sealed abstract class SmType
object SmType {
  case object Honest extends SmType
  case object HalfMiss extends SmType
  case object AlwaysMiss extends SmType
}

/**
 * Enum: the kind of application ("UI").
 */
sealed abstract class AppType
object AppType {
  case object SillyBot extends AppType
  case object DecentBot extends AppType
  case object Console extends AppType
  case object Gui extends AppType
}
 
object Battleship {
  private[this] def rdebug(msg: String) = Debug.debug("battleship")(msg)
  /**
   * <p>Creates a new name server. The nameserver serves as a DNS-like 
   * communication port that permits a new client to ask this nameserver for the 
   * Id of its manager given the tournament's String name.
   * 
   * <p>The intended use has ONE name server per transport layer, and the name
   * server must be assigned Id.coordinatorId such that all clients can
   * find this name server to communicate with it.
   * 
   * @param useRPC if this is an RPC variant
   * @param id transport-layer assigned identification
   * @param outbox a sink for sending outgoing messages
   */
   def createNameServer(useRPC : Boolean)(id : Id, outbox : Sink.T) : Source.T =
     if (useRPC) new RPCBSNameServer(id, outbox)
     else        new    BSNameServer(id, outbox)
   
   /**
    * <p>Creates a new room manager. A room manager runs a tournament room
    * that plays a sequence of games involving the same players. The manager 
    * registers itself with the name server upon construction and there may be
    * multiple managers, each running a tournament, on any given transport
    * layer at a time.
    * 
    * @param name the name of this room (== name of this tournament)
    * @param games a list of games to be played within
    * @param numPlayers number of players in this tournament/room
    * @param useRPC if this is an RPC variant
    * @param id transport-layer assigned identification
    * @param outbox a sink for sending outgoing messages
    */
   def createManager(name: String)(games: List[GameConfig])(numPlayers: Int)(useRPC : Boolean)(id : Id, outbox : Sink.T) : Source.T =
       if (useRPC) new RPCBSManager(name, games, numPlayers, id, outbox)
       else        new BSManager(name, games, numPlayers, id, outbox)
   
   /**
    * Creates a new state machine.
    * 
    * @param roomName the name of the tournament to join
    * @param kind the desired qualities of this state machine (e.g. honest, faulty)  
    * @param useRPC if this is an rpc variant
    * @param id transport-layer assigned identification
    * @param outbox a sink for sending outgoing messages
    * 
    * @see SmType
    */
   def createStateMachine(roomName : String)(kind : SmType)(useRPC: Boolean)(id : Id, outbox : Sink.T) : Source.T  = {
     import edu.upenn.seas.peerreview.smui.variants._
     val logic = kind match {
       case SmType.Honest => new BattleshipLogicImpl()
       case SmType.AlwaysMiss => new BattleshipLogicImpl() with AlwaysMisses
       case SmType.HalfMiss => new BattleshipLogicImpl() with HalfMisses
     }
     if (useRPC)
       new RPCBSSM(logic, id, outbox, roomName)
     else
       new BSSM(logic, id, outbox, roomName)
   }

   /**
    * Creates a new application object ("UI"). It must be paired to an
    * associated state machine.
    * 
    * @param smId the state machine it displays for
    * @param kind the desired qualities of this application object (e.g. bot, gui)  
    * @param useRPC whether to use the RPC variant or not
    * @param id transport-layer assigned identification
    * @param useRPC use rpc msg protocal TODO FIX
    * @param outbox a sink for sending outgoing messages
    * 
    * @see AppType
    */
   def createApplication(smId : Id)(kind : AppType)(useRPC : Boolean)(rndSeed : Int)(id : Id, outbox : Sink.T) : Source.T = {
     import edu.upenn.seas.peerreview.smui.variants._
     val decider = kind match {
       case AppType.Gui => new SwingDecider()
       case AppType.Console => new ConsoleDecider(Console.in, Console.out)
       case AppType.DecentBot => {
         rdebug("starting decider with seed " + rndSeed)
         new DecentBotDecider(new scala.util.Random(rndSeed))
       }
       case _ => new SillyBotDecider()
     }
     if (useRPC)
       new RPCDecisionAdapter(smId, decider, id, outbox)  
     else
       new DecisionAdapter(smId, decider, id, outbox)  
   }
}

/**
 * Encapsulation of game specifications.
 * 
 * @param boardSize the size of the gameboard in this tournament
 * @param ships list of ship sizes in this tournament
 */
case class GameConfig(boardSize : (Int, Int), ships : List[Int])
